/*
*  server.c
*  
*
*  Created by Viktor Kurz & Stefan Wrobel on 08.12.09.
*  Copyright 2009. All rights reserved.
*
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>
#include "dlinkedlist.h"

#include "chat.h"

#define MAX_CLIENTS	30
#define MSG_BUF_SIZE	20

struct client_entry {
	struct sockaddr_in client_sock;
	char name[NAMESIZE];
};

int main (int argc, const char * argv[]) {
	
	int sock;							/* Server-Socket-Deskriptor */
	int len;							/* Rückgabewert von sendto()- bzw. recvfrom()-Aufrufen */
	int i;								/* Zählvariablen */
	int flag;							/* Flag-Variable */
	
	struct sockaddr_in server_addr;  				/* Server-Socket */
	struct sockaddr_in client_addr; 				/* Client-Socket */
	unsigned int client_addr_len = sizeof(struct sockaddr);		/* Variable für Größe des Client-Sockets */
	
	struct chat_message message;					/* Erhaltene bzw. gesendete Nachricht */
	struct chat_message *temp_chat_message;				/* Temporäre Nachricht  (zum anlegen/löschen verwedet) */
	struct DLinkedList *msg_buffer = createDLinkedList();		/* Nachrichten-Puffer */
	char buffer[sizeof(message)] = "";				/* Temporärer Char-Buffer */
	
	struct DLinkedList *client_list = createDLinkedList();		/* Liste der Clients */
	struct client_entry *temp_client_entry;				/* Temporärer Client-Eintrag (zum anlegen/löschen verwedet) */
	
	/* Initialisiertung Server-Socket */
	server_addr.sin_family = AF_INET;
	server_addr.sin_port = htons(8080);
	server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	
	/* Uhrzeit */
	time_t rawtime;
	struct tm * ptm;
	
	/* Socket anlegen */
	if((sock =  socket(AF_INET , SOCK_DGRAM, 0)) == -1) {
		perror("Fehler bei socket!");
	}
	
	/* Bind */
	if(bind(sock, (struct sockaddr *) &server_addr, sizeof(struct sockaddr_in)) == -1) {
		perror("Fehler bei bind!");
	}
	
	/* Hauptschleife */
	do {
		/* Auf Nachricht warten */
		if((len = recvfrom(sock, (char*) &message, sizeof(message), 0, (struct sockaddr *) &client_addr, &client_addr_len)) == -1) {
			perror("Fehler bei recvfrom!");
		}
		
		switch (message.type) {
			
			case REGISTER:
				printf("%s will sich registrieren...\n", message.data);
				/* Registrierung nur möglich, wenn MAX_CLIENTS noch nicht erreicht wurde */
				if(client_list->getSize(client_list) < MAX_CLIENTS - 1) {
					flag = 0;
					/* Durch Liste iterieren; Flag wird um eins erhöht, falls Client in der Liste gefunden wurde */
					for(i = 0; i < client_list->getSize(client_list); i++) {
						if(strcmp(inet_ntoa( ((struct client_entry*)(client_list->getElement(client_list, i)))->client_sock.sin_addr), inet_ntoa(client_addr.sin_addr) ) == 0 &&  \
							ntohs(((struct client_entry*)(client_list->getElement(client_list, i)))->client_sock.sin_port) ==  ntohs(client_addr.sin_port))
						{
							flag++;
							break;
						}
					}
					/* Client noch nicht in der Liste gespeichert, Registrierung ist möglich */
					if(!flag) {
						/* Neuer client_entry anlegen, Daten (Name) aus message kopieren und zur Liste hinzufügen */
						struct client_entry* newClient = malloc(sizeof(struct client_entry));
						memcpy(&newClient->client_sock, &client_addr, sizeof(client_addr));
						memcpy(newClient->name, message.data, sizeof(newClient->name));
						client_list->addElement(client_list, (char*) newClient);

						/* Liste mit Namen aller registrierten Clients erstellen */
						message.type = NAMELIST;
						strcpy(message.data, "\0");
						for(i = 0; i < client_list->getSize(client_list); i++) {
							/* Namen sind durch Kommas getrennt */
							sprintf(message.data, "%s,%s", message.data, ((struct client_entry*) client_list->getElement(client_list, i))->name);
						}
						
						/*  Liste an alle registrierten Clients versenden */
						printf("Versende Namensliste: %s\n", message.data);
						for(i = 0; i < client_list->getSize(client_list); i++) {
							if((len = sendto(sock, (char*) &message, sizeof(message.type) + strlen(message.data)+1, 0,(struct sockaddr*) &((struct client_entry*) client_list->getElement(client_list, i))->client_sock, sizeof(struct sockaddr_in))) == -1){
								perror("Fehler bei sendto!");
							}
						}
						
						/* Die letzen Nachrichten, maximal MSG_BUF_SIZE, an den neuen Client senden */
						message.type = MESSAGE;
						printf("Sende die letzten Nachrichten an den neu registrierten...\n");
						for(i = 0;  i < msg_buffer->getSize(msg_buffer); i++) {
							message.data[0] = '\0';
							strncpy(message.data, ((struct chat_message*)(msg_buffer->getElement(msg_buffer, i)))->data, strlen(((struct chat_message*)(msg_buffer->getElement(msg_buffer, i)))->data)+1);
							printf("Nachricht %d: %s\n", i, message.data);
							if((len = sendto(sock, (char*) &message, sizeof(message.type) + strlen(message.data)+1, 0, (struct sockaddr*) &client_addr, sizeof(struct sockaddr_in))) == -1){
								perror("Fehler bei sendto!");
							}	
						}
					} else { 
						/* Client ist bereits angemeldet, Fehlernachricht wird zurückgeschickt */
						message.type = ERROR;
						sprintf(message.data, "Client bereits registriert!");
						if((len = sendto(sock, (char*) &message, sizeof(message.type) + strlen(message.data)+1, 0,(struct sockaddr*) &client_addr, sizeof(struct sockaddr_in))) == -1){
							perror("Fehler bei sendto!");
						}
					}
				} else {
					/* Maximale Anzahl registrierter Clients bereits erreicht, Fehlernachricht wird zurückgeschickt  */
					message.type = ERROR;
					sprintf(message.data, "Maximale Anzahl registrierter Clients bereits erreicht!");
					if((len = sendto(sock, (char*) &message, sizeof(message.type) + strlen(message.data)+1, 0, (struct sockaddr*) &client_addr, sizeof(struct sockaddr_in))) == -1){
						perror("Fehler bei sendto!");
					}
				}
				break;
				
			case UNREGISTER:
				for(i = 0; i < client_list->getSize(client_list); i++) {
					/* Client kann Registrierung nur zurücknehmen, wenn er sich zuvor registriert hat */
					if(strcmp(inet_ntoa( ((struct client_entry*)(client_list->getElement(client_list, i)))->client_sock.sin_addr), inet_ntoa(client_addr.sin_addr) ) == 0 &&  \
						ntohs(((struct client_entry*)(client_list->getElement(client_list, i)))->client_sock.sin_port) ==  ntohs(client_addr.sin_port))
					{
						/* Client wurde in der Liste gefunden, Eintrag wird aus der Liste gelöscht und dessen client_entry freigegeben */
						temp_client_entry = (struct client_entry*) client_list->removeAtPosition(client_list, i);
						printf("Client meldet sich ab: %s\n", temp_client_entry->name);
						if(temp_client_entry != NULL) {
							free(temp_client_entry);
						}
						break;
					}
				}

				/* Neue Namensliste versenden (s.o.) */
				message.type = NAMELIST;
				strcpy(message.data, "\0");
				for(i = 0; i < client_list->getSize(client_list); i++) {
					sprintf(message.data, "%s,%s", message.data, ((struct client_entry*) client_list->getElement(client_list, i))->name);
				}
				printf("Versende Namensliste: %s\n", message.data);
				for(i = 0; i < client_list->getSize(client_list); i++) {
					if((len = sendto(sock, (char*) &message, sizeof(message.type) + strlen(message.data)+1, 0,(struct sockaddr*) &((struct client_entry*) client_list->getElement(client_list, i))->client_sock, sizeof(struct sockaddr_in))) == -1){
						perror("Fehler bei sendto!");
					}
				}
				break;
				
			case MESSAGE:
				for(i = 0; i < client_list->getSize(client_list) ; i++) {
					/* Nachricht nur annehmen, wenn der Client registriert ist */
					if(strcmp(inet_ntoa( ((struct client_entry*)(client_list->getElement(client_list, i)))->client_sock.sin_addr), inet_ntoa(client_addr.sin_addr) ) == 0 &&  \
						ntohs(((struct client_entry*)(client_list->getElement(client_list, i)))->client_sock.sin_port) ==  ntohs(client_addr.sin_port))
					{
						printf("Erhalte Nachricht von %s: %s\n", ((struct client_entry*)(client_list->getElement(client_list, i)))->name, message.data);
						printf("Nachricht hat die Länge %d\n", (int)strlen(message.data));
						buffer[0] = '\0';
						time(&rawtime);
						ptm = gmtime(&rawtime);
						message.data[sizeof(message.data)-1 - strlen(((struct client_entry*)(client_list->getElement(client_list, i)))->name) - 11] = '\0';
						sprintf(buffer, "%s (%02d:%02d): %s", ((struct client_entry*)(client_list->getElement(client_list, i)))->name, ptm->tm_hour+1, ptm->tm_min, message.data);
						strcpy(message.data, buffer);
						printf("Folgendes wird an alle Clients verschickt: %s\n", message.data);
				
						/* Neue Nachricht an Clients verschicken */
						for(i = 0; i < client_list->getSize(client_list); i++) {
							printf("Sende neu erhaltene Nachricht an %s\n",  ((struct client_entry*)(client_list->getElement(client_list, i)))->name);
							if((len = sendto(sock, (char*) &message, sizeof(message.type) + strlen(message.data)+1, 0,(struct sockaddr*) &((struct client_entry*) client_list->getElement(client_list, i))->client_sock, sizeof(struct sockaddr_in))) == -1){
								perror("Fehler bei sendto!");
							}						
						}
						
						/* Erhaltene Nachrigt in msg_buffer speichern */
						temp_chat_message = malloc(sizeof(struct chat_message));
						memcpy(temp_chat_message, &message, sizeof(struct chat_message));
						msg_buffer->addLast(msg_buffer, (char*) temp_chat_message);

						/* Falls MSG_BUF_SIZE Nachrichten im Puffer, Lösche die älteste */
						if(msg_buffer->getSize(msg_buffer) > MSG_BUF_SIZE) {
							temp_chat_message = (struct chat_message*) msg_buffer->removeFirst(msg_buffer);
							if(temp_chat_message != NULL) {
								free(temp_chat_message);
							}
						}
						
						break;
					}
				}
				break;
				
			default:
				/* Nichts */
				break;
		}
	
	} while(1); /* TODO: Signal-Handler für Ctrl-C -> freigeben msg_buffer + client_list */
	
	return 0;
}